package android.slc.db.service;

import android.slc.adapter.PageModel;
import android.slc.db.entity.IDbBase;
import android.slc.db.entity.IDbEntity;
import android.slc.db.exception.DbEntityServiceException;
import android.slc.hutool.compat.core.util.BeanCompatUtil;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.blankj.utilcode.util.CollectionUtils;
import com.blankj.utilcode.util.MapUtils;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;

/**
 * @param <T>
 * @author slc
 * 自己搭建的基于map的持久话数据库
 */
public interface DbEntityMapper<T extends IDbEntity<? extends Serializable>> {

    /**
     * 获取当前的VmClass
     *
     * @return
     */
    @SuppressWarnings("unchecked")
    default Class<T> getThisEntityClass() {
        return getThisEntityClass(0);
    }

    @SuppressWarnings("unchecked")
    default Class<T> getThisEntityClass(int typeIndex) {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            try {
                Class<T> tClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[typeIndex];
                return tClass;
            } catch (ArrayIndexOutOfBoundsException e) {
                throw new DbEntityServiceException("泛型丢失");
            }
        } else {
            throw new DbEntityServiceException("没有实现泛型");
        }
    }

    default void put(@Nullable Collection<T> entities) {
        Map<String, T> entityMap = getAllPrivate();
        CollectionUtils.forAllDo(entities, (index, item) -> {
            checkEntity(item);
            entityMap.put(item.getEId().toString(), item);
        });
        synchronousData();
    }

    /**
     * 较为消耗性能
     *
     * @param entities
     */
    default void putNotify(@Nullable Collection<T> entities) {
        if (entities != null && !entities.isEmpty()) {
            List<T> updateList = new ArrayList<>();
            List<T> putList = new ArrayList<>();
            for (T t : entities) {
                Serializable idTemp = getId(t);
                if (idTemp != null && getById(idTemp) != null) {
                    put(t);
                    //updateList.add(get(put(t)));
                    updateList.add(t);
                } else {
                    put(t);
                    putList.add(t);
                }
            }
            notifyUpdate(updateList);
            notifyPut(putList);
        }
    }

    default void put(@Nullable T... entities) {
        if (ArrayUtil.isEmpty(entities)) {
            return;
        }
        put(Arrays.asList(entities));
    }

    /**
     * 较为消耗性能
     *
     * @param entities
     */
    default void putNotify(@Nullable T... entities) {
        if (ArrayUtil.isEmpty(entities)) {
            return;
        }
        putNotify(Arrays.asList(entities));
    }

    default Serializable put(@Nullable T entity) {
        if (entity == null) {
            return null;
        }
        Map<String, T> entityMap = getAllPrivate();
        T findResult = entityMap.get(entity.getEId());
        checkEntity(entity);
        //此处暂时写一样的
        if (findResult != null) {
            //更新
            entityMap.put(entity.getEId().toString(), entity);
        } else {
            //添加
            entityMap.put(entity.getEId().toString(), entity);
        }
        synchronousData();
        return entity.getEId();
    }

    /**
     * 较为消耗性能
     *
     * @param entity
     * @return
     */
    default Serializable putNotify(@Nullable T entity) {
        if (entity == null) {
            return null;
        }
        boolean isUpdate;
        //此处查找方式不准确，但也是没有办法的办法
        Serializable id = getId(entity);
        isUpdate = id != null;
        id = put(entity);
        if (isUpdate) {
            notifyUpdate(entity);
        } else {
            notifyPut(entity);
        }
        return id;
    }


    default Serializable getId(@Nullable T entity) {
        if (entity == null || entity.getEId() == null) {
            return null;
        }
        T entityTemp = getById(entity.getEId());
        if (entityTemp == null) {
            return null;
        }
        return entityTemp.getEId();
    }

    default T getById(@Nullable Serializable id) {
        return getById(id, null);
    }

    default <S extends Serializable> T getById(@Nullable S id, OnCreateListener<T, S> onCreateListener) {
        if (id == null) {
            return null;
        }
        T dbEntity = getAllPrivate().get(id);
        if (dbEntity == null && onCreateListener != null) {
            dbEntity = onCreateListener.onCreate(id);
            put(dbEntity);
        }
        return BeanCompatUtil.copyProperties(dbEntity, getThisEntityClass());
    }

    default List<T> getByIds(@Nullable Collection<? extends Serializable> ids) {
        Map<String, T> allMap = getAllPrivate();
        List<T> resultList = new ArrayList<>();
        CollectionUtils.forAllDo(ids, (index, item) -> {
            T entity = allMap.get(item);
            if (entity != null) {
                resultList.add(entity);
            }
        });
        return BeanCompatUtil.copyToList(resultList, getThisEntityClass());
    }

    default List<T> getByIds(@Nullable Serializable[] ids) {
        if (ArrayUtil.isEmpty(ids)) {
            return null;
        }
        return getByIds(Arrays.asList(ids));
    }

    default Map<Serializable, T> getMap(@Nullable Collection<? extends Serializable> ids) {
        List<T> idDataList = getByIds(ids);
        Map<Serializable, T> idDataMap = new HashMap<>();
        CollectionUtils.forAllDo(idDataList, (index, item) -> {
            idDataMap.put(item.getEId(), BeanCompatUtil.copyProperties(item, getThisEntityClass()));
        });
        return idDataMap;
    }


    default T getOne(@Nullable OnSelectConditionListener<T> onSelectConditionListener) {
        T dbEntity = null;
        if (onSelectConditionListener != null) {
            dbEntity = CollectionUtil.findOne(getAllList(), entityItem -> onSelectConditionListener.onQueryCondition(entityItem, null));
        }
        return BeanCompatUtil.copyProperties(dbEntity, getThisEntityClass());
    }

    default T getOne(@Nullable T entity, @Nullable OnSelectConditionListener<T> onSelectConditionListener) {
        return getOne(entity, onSelectConditionListener, null);
    }

    default T getOne(@Nullable T entity, @Nullable OnSelectConditionListener<T> onSelectConditionListener, @Nullable OnCreateListener<T, T> onCreateListener) {
        T dbEntity = CollectionUtil.findOne(getAllList(), entityItem -> onSelectConditionListener.onQueryCondition(entityItem, entity));
        if (dbEntity == null && onCreateListener != null) {
            dbEntity = onCreateListener.onCreate(entity);
            put(dbEntity);
        }
        return BeanCompatUtil.copyProperties(dbEntity, getThisEntityClass());
    }

    default boolean existById(@Nullable Serializable id) {
        return getById(id) != null;
    }

    default List<T> select(@Nullable OnSelectConditionListener<T> onSelectConditionListener) {
        return select(null, onSelectConditionListener);
    }

    default List<T> select(@Nullable T entity, @Nullable OnSelectConditionListener<T> onSelectConditionListener) {
        List<T> dbEntityList = CollectionUtil.newArrayList(CollectionUtil.filterNew(getAllList(), entityItem -> onSelectConditionListener.onQueryCondition(entityItem, entity)));
        return BeanCompatUtil.copyToList(dbEntityList, getThisEntityClass());
    }

    default PageModel<T> selectByPage(int current, int size) {
        List<T> dbEntityList = getAllList();
        return PageModel.createPageModel(current, size, dbEntityList);
    }

    default PageModel<T> selectByPage(int current, int size, @Nullable OnSelectConditionListener<T> onSelectConditionListener) {
        List<T> dbEntityList = select(onSelectConditionListener);
        return PageModel.createPageModel(current, size, dbEntityList);
    }

    default PageModel<T> selectByPage(int current, int size, @Nullable T entity, @Nullable OnSelectConditionListener<T> onSelectConditionListener) {
        List<T> dbEntityList = select(entity, onSelectConditionListener);
        return PageModel.createPageModel(current, size, dbEntityList);
    }

    default long count() {
        return getAllPrivate().size();
    }

    default long count(long maxCount) {
        long count = count();
        return Math.min(count, maxCount);
    }

    default boolean isEmpty() {
        return count(1) == 0;
    }

    /**
     * 不要调用这个，临时先这么写，后续优化
     *
     * @return
     */
    Map<String, T> getAllPrivate();

    default Map<String, T> getAll() {
        final Map<String, T> publicMap = new LinkedHashMap<>();
        MapUtils.forAllDo(publicMap, new MapUtils.Closure<String, T>() {
            @Override
            public void execute(String key, T value) {
                publicMap.put(key, BeanCompatUtil.copyProperties(value, getThisEntityClass()));
            }
        });
        return publicMap;
    }

    default List<T> getAllList() {
        return BeanCompatUtil.copyToList(CollectionUtil.list(false, getAllPrivate().values()), getThisEntityClass());
    }

    default T removeById(@Nullable Serializable id) {
        if (id == null) {
            return null;
        }
        T resultData = getAllPrivate().remove(id);
        if (resultData != null) {
            synchronousData();
        }
        return resultData;
    }

    default T removeNotifyById(@Nullable Serializable id) {
        T resultData = removeById(id);
        notifyRemove(resultData);
        return resultData;
    }

    default List<T> removeByIds(@Nullable Serializable... ids) {
        if (ArrayUtil.isEmpty(ids)) {
            return null;
        }
        return removeByIds(Arrays.asList(ids));
    }

    default List<T> removeNotifyByIds(@Nullable Serializable... ids) {
        List<T> entities = removeByIds(ids);
        notifyRemove(entities);
        return entities;
    }

    default List<T> removeByIds(@Nullable Collection<? extends Serializable> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return null;
        }
        List<T> resultList = new ArrayList<>();
        for (Serializable id : ids) {
            T resultData = removeById(id);
            if (resultData != null) {
                resultList.add(resultData);
            }
        }
        synchronousData();
        return resultList.isEmpty() ? null : resultList;
    }

    default List<T> removeNotifyByIds(@Nullable Collection<? extends Serializable> ids) {
        List<T> entities = removeByIds(ids);
        notifyRemove(entities);
        return entities;
    }

    default T remove(@Nullable T entity) {
        if (entity == null) {
            return null;
        }
        return removeById(entity.getEId());
    }

    default T removeNotify(@Nullable T entity) {
        T resultData = remove(entity);
        notifyRemove(entity);
        return resultData;
    }

    default List<T> remove(@Nullable T... entities) {
        if (ArrayUtil.isEmpty(entities)) {
            return null;
        }
        return remove(Arrays.asList(entities));
    }

    default List<T> removeNotify(@Nullable T... entities) {
        if (ArrayUtil.isEmpty(entities)) {
            return null;
        }
        return removeNotify(Arrays.asList(entities));
    }

    default List<T> remove(@Nullable Collection<T> entities) {
        if (ArrayUtil.isEmpty(entities)) {
            return null;
        }
        List<Serializable> ids = new ArrayList<>();
        CollectionUtils.forAllDo(entities, (index, item) -> {
            if (item != null) {
                ids.add(item.getEId());
            }
        });
        return removeByIds(ids);
    }

    default List<T> removeNotify(@Nullable Collection<T> entities) {
        List<T> resultList = remove(entities);
        notifyRemove((List<T>) entities);
        return resultList;
    }

    default void removeAll() {
        getAllPrivate().clear();
        synchronousData();
    }

    /**
     * 检查必要参数
     *
     * @param entity
     */
    default void checkEntity(T entity) {
        if (entity instanceof IDbBase) {
            IDbBase iDbBase = (IDbBase) entity;
            if (iDbBase.get_createTime() == null) {
                iDbBase.set_createTime(System.currentTimeMillis());
            }
            Serializable idTemp = getId(entity);
            if (idTemp != null) {
                iDbBase.set_updateTime(System.currentTimeMillis());
            }
        }
    }

    /**
     * 检查必要参数
     *
     * @param entities
     */
    default void checkEntity(T... entities) {
        if (entities != null) {
            for (T entity : entities) {
                checkEntity(entity);
            }
        }
    }

    /**
     * 检查必要参数
     *
     * @param entities
     */
    default void checkEntity(Collection<T> entities) {
        if (entities != null) {
            for (T entity : entities) {
                checkEntity(entity);
            }
        }
    }

    default void synchronousData() {
        synchronousData(getAllPrivate());
    }

    void synchronousData(Map<String, T> entityList);

    default void notifyPut(T entity) {
        if (entity != null) {
            notifyPut(Collections.singletonList(entity));
        }
    }

    void notifyPut(List<T> entityList);

    default void notifyUpdate(T entity) {
        if (entity != null) {
            notifyUpdate(Collections.singletonList(entity));
        }
    }

    void notifyUpdate(List<T> entityList);

    default void notifyRemove(T entity) {
        if (entity != null) {
            notifyRemove(Collections.singletonList(entity));
        }
    }

    void notifyRemove(List<T> list);

    void addDbChangeListener(@NonNull OnDbChangeListener<T> onDbChangeListener);

    void removeOnDbChangeListener(@NonNull OnDbChangeListener<T> onDbChangeListener);

    public interface OnCreateListener<T extends IDbEntity<? extends Serializable>, S> {
        T onCreate(S queryCondition);
    }

    public interface OnSelectConditionListener<T extends IDbEntity<? extends Serializable>> {
        boolean onQueryCondition(@NonNull T targetData, @Nullable T queryCondition);
    }

    public interface OnSelectListener<T extends IDbEntity<? extends Serializable>> {
        boolean onQuery(T targetData);
    }

    interface OnDbChangeListener<T> {
        void onChange(@Action int action, List<T> data);

        @Action
        List<Integer> getAction();
    }

    interface DaoElementListener<T> {
        void onRemove(int dataDiversityVersion, T data);

        void onPut(int dataDiversityVersion, T data);
    }

    @interface Action {
        int ALL = -100;
        int PUT = -101;
        int UPDATE = -102;
        int DELETE = -103;
    }
}
